// xredis
package main

import (
	"xredis/color"
	//"encoding/json"
	"flag"
	"fmt"

	"strconv"
	"strings"
	"time"

	"bufio"

	"github.com/garyburd/redigo/redis"
)

var rediscli redis.Conn

func ParseLine(line string) string {
	if !strings.Contains(line, "noaddr") {
		var role = "slave"
		var status = "connected"
		if strings.Contains(line, "master") {
			role = "master"
		}
		if strings.Contains(line, "disconnected") {
			status = "disconnected"
		}
		ssLine := strings.Fields(line)
		redisip := strings.Split(ssLine[1], "@") //兼容redis5.0以上
		//lengthArry := len(ssLine)
		return fmt.Sprintf("%s,%s,%s", redisip[0], role, status)
	}
	return ""
}

func GetAllNode() []string {
	var clusterArry []string
	resp, err := redis.String(rediscli.Do("cluster", "nodes"))
	if err != nil {
		fmt.Println("redis get failed:", err)
		return clusterArry
	} else {
		respReader := strings.NewReader(fmt.Sprintf("%s", resp))
		bi := bufio.NewReader(respReader)
		for {
			line, err := bi.ReadString('\n')
			if err != nil {
				break // 读取完成整个文件退出循环
			}
			resultstr := ParseLine(line)
			if resultstr == "" {
				continue
			}
			clusterArry = append(clusterArry, resultstr)
		}
		return clusterArry
	}
}

func GetInfo(cli redis.Conn, key string) map[string]string {
	resp, err := redis.String(cli.Do("info", key))
	if err != nil {
		fmt.Println("redis get failed:", err)
		return nil
	} else {
		var vMap = make(map[string]string)
		respReader := strings.NewReader(resp)
		bi := bufio.NewReader(respReader)
		for {
			line, err := bi.ReadString('\n')
			if err != nil {
				break // 读取完成整个文件退出循环
			}
			//fmt.Println(line + "ok")
			vvarry := strings.Split(line, ":")
			if len(vvarry) == 2 {
				//fmt.Println(vvarry[0] + "," + strings.TrimSpace(vvarry[1]) + "ok")
				vMap[vvarry[0]] = strings.TrimSpace(vvarry[1])
			}
		}
		return vMap
	}
}

func ListClusterStatus() {
	nodeArry := GetAllNode()
	fmt.Println("	RedisIp\t	Used\t	UsedRss\t	UsedPeak	MaxMem\t	SysMem\t	Role\t	Status")
	for _, vv := range nodeArry {
		ipArry := strings.Split(vv, ",")
		ipstr := ipArry[0]
		roles := ipArry[1]
		status := ipArry[2]
		cli, err := redis.DialTimeout("tcp", ipstr, T_out, T_out, T_out)
		if err != nil {
			fmt.Println("Connect to redis error", err)
			return
		}
		defer rediscli.Close()

		mm := GetInfo(cli, "Memory")
		fmt.Printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", ipstr, mm["used_memory_human"], mm["used_memory_rss_human"], mm["used_memory_peak_human"], mm["maxmemory_human"], mm["total_system_memory_human"], roles, status)
	}
}

func FlushAll(cluster bool) {
	T_out := time.Duration(time.Second * 10)
	if cluster {
		nodeArry := GetAllNode()
		for _, vv := range nodeArry {
			ipArry := strings.Split(vv, ",")
			ipstr := ipArry[0]
			//roles := ipArry[1]
			//status := ipArry[2]
			cli, err := redis.DialTimeout("tcp", ipstr, T_out, T_out, T_out)
			if err != nil {
				fmt.Println("Connect to redis error", err)
				return
			}
			defer rediscli.Close()
			resp, err := redis.String(cli.Do("FLUSHALL"))
			if err != nil {
				fmt.Printf("%s flushall failed:%s\n", ipstr, err)
			} else {
				fmt.Printf("%s flushall %s\n", ipstr, resp)
			}
		}
	} else {
		resp, err := redis.String(rediscli.Do("FLUSHALL"))
		if err != nil {
			fmt.Printf("flushall failed:%s\n", err)
		} else {
			fmt.Printf("flushall %s\n", resp)
		}
	}
}

func DelRedisKey(key string) {
	resp, err := redis.Int(rediscli.Do("DEL", key))
	if err != nil {
		fmt.Printf("delete failed:%s\n", err)
	} else {
		fmt.Printf("delete ok:%d\n", resp)
	}
}

func Runcommand(cmd string, args []interface{}) {
	resp, err := redis.String(rediscli.Do(cmd, args...))
	if err != nil {
		fmt.Printf("run command failed:%s\n", err)
	} else {
		fmt.Printf("%s\n", resp)
	}
}

func PrintRedisInfo() {
	m1 := GetInfo(rediscli, "Server")
	m2 := GetInfo(rediscli, "Clients")
	m3 := GetInfo(rediscli, "Memory")
	//m4 := GetInfo(client, "Persistence")
	m5 := GetInfo(rediscli, "Stats")
	m6 := GetInfo(rediscli, "Replication")
	m7 := GetInfo(rediscli, "CPU")

	color.Cyan("  redis_version             : %-12s", m1["redis_version"])
	color.Green("|   redis_mode                : %s\n", m1["redis_mode"])
	color.Cyan("  uptime_days               : %-12s", m1["uptime_in_days"])
	color.Green("|   config_file               : %s\n", m1["config_file"])
	color.Cyan("  connected_clients         : %-12s", m2["connected_clients"])
	color.Green("|   blocked_clients           : %s\n", m2["blocked_clients"])
	color.Cyan("  used_memory               : %-12s", m3["used_memory_human"])
	color.Green("|   used_memory_peak          : %s\n", m3["used_memory_peak_human"])

	var vv, v2 string
	var ok, ok2 bool
	if vv, ok = m3["maxmemory_human"]; !ok {
		vv = ""
	}
	color.Cyan("  maxmemory_human           : %-12s", vv)

	if v2, ok2 = m3["total_system_memory_human"]; !ok2 {
		v2 = ""
	}
	color.Green("|   total_system_memory_human : %s\n", v2)

	inByte, _ := strconv.ParseFloat(m5["total_net_input_bytes"], 64)

	outByte, _ := strconv.ParseFloat(m5["total_net_output_bytes"], 64)
	color.Cyan("  input_bytes[GB]           : %-12.2f", inByte/1024/1024/1024)
	color.Green("|   output_bytes[GB]          : %0.2f\n", outByte/1024/1024/1024)
	color.Cyan("  instantaneous_input_kbps  : %-12s", m5["instantaneous_input_kbps"])
	color.Green("|   instantaneous_output_kbps : %s\n", m5["instantaneous_output_kbps"])
	color.Cyan("  expired_keys              : %-12s", m5["expired_keys"])
	color.Green("|   evicted_keys              : %s\n", m5["evicted_keys"])
	color.Cyan("  role                      : %-12s", m6["role"])
	color.Green("|   connected_slaves          : %s\n", m6["connected_slaves"])
	color.Cyan("  used_cpu_sys              : %-12s", m7["used_cpu_sys"])
	color.Green("|   used_cpu_user             : %s\n", m7["used_cpu_user"])
}

func Help() {
	color.Cyan("Version: xredis_v2.0\n")
	fmt.Printf("\nBuildTime: " + VERSION)
	fmt.Printf("\nUsage:./xredis\n")
	fmt.Printf("Usage:./xredis -i=\"192.168.1.1\" -p=\"6379\" -w=\"password\"\n")
	fmt.Printf("Usage:./xredis -info=Server [args is (Server|Clients|Memory|Persistence|Stats|Replication|CPU)]\n")
	fmt.Printf("Usage:./xredis -del=\"key\" [delete key]\n")
	fmt.Printf("Usage:./xredis -cmd=\"get key\" [send command to redis]\n")
	fmt.Printf("Usage:./xredis -c=false [show color]\n")
	fmt.Printf("Usage:./xredis -flushall=true [flushall stand-alone redis]\n")
	fmt.Printf("Usage:./xredis -flushall=true -cluster=true [flushall cluster redis]\n")
	fmt.Printf("Usage:./xredis -list=true [list cluster redis]\n")
	fmt.Printf("defautl ip=127.0.0.1 default port=6379\n")
}

var T_out = time.Duration(time.Second * 10)
var VERSION string

func main() {
	help := flag.Bool("h", false, "show help")
	ip := flag.String("i", "127.0.0.1", "redis server ip")
	port := flag.String("p", "6379", "redis server port")
	password := flag.String("w", "", "redis server password")
	showColor := flag.Bool("c", true, "show or not show color")
	xkey := flag.String("info", "", "Server|Clients|Memory|Persistence|Stats|Replication|CPU")
	delkey := flag.String("del", "", "delete key")
	runcmd := flag.String("cmd", "", "run command")
	flushsingle := flag.Bool("flushall", false, "flushall stand-alone redis")
	flushcluster := flag.Bool("cluster", false, "flushall cluster redis")

	listCluster := flag.Bool("list", false, "list cluster redis")

	flag.Parse()

	if *showColor {
		color.Enable()
	}

	if *help {
		Help()
		return
	}
	var err error
	var ipaddr string
	if !strings.Contains(*ip, "192.168") && len(strings.Split(*ip, ".")) == 2 {
		ipaddr = "192.168." + *ip
	} else {
		ipaddr = *ip
	}
	//rediscli, err = redis.DialTimeout("tcp", *ip+":"+*port, T_out, T_out, T_out)
	rediscli, err = redis.Dial("tcp", ipaddr+":"+*port, redis.DialPassword(*password))
	if err != nil {
		fmt.Println("Connect to redis error", err)
		return
	}
	defer rediscli.Close()

	if *listCluster {
		ListClusterStatus()
		return
	}

	if *flushsingle {
		FlushAll(*flushcluster)
		return
	}

	if *xkey != "" {
		for kk, vv := range GetInfo(rediscli, *xkey) {
			fmt.Printf("%s:%s\n", kk, vv)
		}
		return
	}

	if *delkey != "" {
		DelRedisKey(*delkey)
		return
	}

	if *runcmd != "" {
		cmdstrArry := strings.Fields(*runcmd)
		cmd := cmdstrArry[0]
		var args []interface{}
		for _, vv := range cmdstrArry[1:] {
			args = append(args, vv)
		}
		Runcommand(cmd, args)
		return
	}

	PrintRedisInfo()
}
